http://www.WizBrother.com/
PRD算法中的数学

PRD算法中的数学

12 人赞同了该文章

PRD算法介绍

上一篇文章介绍了一种简单易懂的保底机制,在本篇文章中对它进行简修改,变成很多游戏中都在使用的PRD算法。下面的网站对PRD算法的由来和细节进行了详细介绍,

PRD算法是一种随机的保底机制,与上一篇文章类似,如果一个事件连续 N1\displaystyle N-1 次没有成功的话,那么第 N\displaystyle N 次一定成功。但与前一篇文章不同的是,在第 N\displaystyle N 次之前的每一次成功概率不是常数,而是会随着失败次数线性增长,直至达到 1\displaystyle 1。具体来说就是,给定常数 0<c<1\displaystyle 0< c< 1,假设在某个时间,距离最近一次成功之后又连续失败了 k1\displaystyle k-1 次,那这次成功的概率是 kc\displaystyle kc ,随着连续失败次数$\displaystyle k$增加,总会有一次成功概率 kc1\displaystyle kc\geqslant 1,那这一次就一定会成功。设 N=min{k|kc1}=min{k|k1/c}=1/c\displaystyle N=\min\{k|kc\geqslant 1\} =\min\{k|k\geqslant 1/c\} =\lceil 1/c\rceil 在这个算法下,事件最多连续失败 N1\displaystyle N-1 次,这说明名义概率 p\displaystyle p 一定不小于 1/N\displaystyle 1/N

这里我们将用上一篇文章介绍的两种方法来找到 c\displaystyle cp\displaystyle p 的关系,学会后大家可以对前面的算法进行修改,例如将线性增长改为非线性变化,并计算出对应的名义概率。

方法1:

1\displaystyle 1 次就成功的概率是 c\displaystyle c,恰好第 2\displaystyle 2 次成功的概率是 (1c)2c\displaystyle ( 1-c) 2c,恰好第 3\displaystyle 3 次成功的概率是 (1c)(12c)3c\displaystyle ( 1-c)( 1-2c) 3c,对于 1kN1\displaystyle 1\leqslant k\leqslant N-1,恰好在第 k\displaystyle k 次成功的概率是 (1c)(12c)(1(k1)c)kc\displaystyle ( 1-c)( 1-2c) \cdots ( 1-( k-1) c) kc,而恰好在第 N\displaystyle N 次才成功,也就是前 N1\displaystyle N-1 次都失败的概率是 (1c)(12c)(1(N1)c)\displaystyle ( 1-c)( 1-2c) \cdots ( 1-( N-1) c)。所以成功所花时间的期望是 E=k=1N1k(1c)(12c)(1(k1)c)kc+N(1c)(12c)(1(N1)c)=ck=1N1k2[j=1k1(1jc)]+Nj=1N1(1jc)\begin{aligned} E & =\sum _{k=1}^{N-1} k( 1-c)( 1-2c) \cdots ( 1-( k-1) c) kc+N( 1-c)( 1-2c) \cdots ( 1-( N-1) c)\\ & =c\sum _{k=1}^{N-1} k^{2}\left[\prod _{j=1}^{k-1}( 1-jc)\right] +N\prod _{j=1}^{N-1}( 1-jc) \end{aligned}\\看起来挺难化简的,所以名义概率 p\displaystyle p p=1E=1ck=1N1k2[j=1k1(1jc)]+Nj=1N1(1jc) p=\frac{1}{E} =\frac{1}{c\sum _{k=1}^{N-1} k^{2}\left[\prod _{j=1}^{k-1}( 1-jc)\right] +N\prod _{j=1}^{N-1}( 1-jc)}\\这是用 c\displaystyle c 计算 p\displaystyle p 的公式。通常我们是指定名义概率 p\displaystyle p,需要求出对应的 c\displaystyle c,可以想象名义概率 p\displaystyle pc\displaystyle c 的严格递增函数,所以利用二分查找即可。很多介绍PRD算法的文章用这个方法给出 p\displaystyle pc\displaystyle c 的对照表

表格数据源于dota2wiki: https://dota2.fandom.com/wiki/Random_Distribution

方法2:

构建Markov链,与上一篇文章一样,状态空间为 S={0,1,2,,N1}\displaystyle S=\{0,1,2,\cdots ,N-1\}N\displaystyle N 个元素, Zt\displaystyle Z_{t} 表示在最近一次成功之后又连续失败了几次,转移图如下

容易看出是不可约,非周期的。转移矩阵P=(c1c2c12c3c13c(N1)c1(N1)c10)012N2N1P=\begin{pmatrix} c & 1-c & & & & & \\ 2c & & 1-2c & & & & \\ 3c & & & 1-3c & & & \\ \vdots & & & & \ddots & & \\ ( N-1) c & & & & & 1-( N-1) c & \\ 1 & & & & & 0 & \end{pmatrix}\begin{matrix} 0\\ 1\\ 2\\ \vdots \\ N-2\\ N-1 \end{matrix} \\唯一平稳分布 π=(π0,π1,,πN1)\displaystyle \pi =( \pi _{0} ,\pi _{1} ,\cdots ,\pi _{N-1}) 满足 {πP=ππ1=1\begin{cases} \pi P=\pi \\ \pi \mathbf{1} =1 \end{cases}\\ 得到{ci=0N2(i+1)πi+πN1=π0(1ic)πi1=πi, i=1,,N1i=0N1πi=1\begin{cases} c\sum\nolimits _{i=0}^{N-2}( i+1) \pi _{i} +\pi _{N-1} =\pi _{0}\\ ( 1-ic) \pi _{i-1} =\pi _{i} ,\ i=1,\cdots ,N-1\\ \sum\nolimits _{i=0}^{N-1} \pi _{i} =1 \end{cases}\\ 仿照上篇文章里的分析过程, πi=π0[j=0i(1jc)]\displaystyle \pi _{i} =\pi _{0}\left[\prod\nolimits _{j=0}^{i}( 1-jc)\right],代入第一个方程得到恒等式ci=0N2(i+1)[j=0i(1jc)]+j=0N1(1jc)1c\sum\nolimits _{i=0}^{N-2}( i+1)\left[\prod\limits _{j=0}^{i}( 1-jc)\right] +\prod\limits _{j=0}^{N-1}( 1-jc) \equiv 1\\列出这个方程的前提条件是 (N1)c<1Nc\displaystyle ( N-1) c< 1\leqslant Nc。不过观察可知,等式的左边是 c\displaystyle c 的多项式,右端是 0\displaystyle 0 次多项式,这两个多项式在区间 c[1/N,1/(N1))\displaystyle c\in [ 1/N,1/( N-1)) 上相等,所以对于任何复数 c\displaystyle c,左边端都是常数 1\displaystyle 1。挺神奇的,我不知道这个等式怎么通过其他方法证明。对于特殊的情况 c=1/N\displaystyle c=1/N,等式变为 11Ni=0N1(i+1)[j=0i(1jN)]=1Ni=0N1(N1)!(i+1)(Ni1)!Ni=1Ni=1Ni×N!(Ni)!Ni\begin{aligned} 1 & \equiv \frac{1}{N}\sum\nolimits _{i=0}^{N-1}( i+1)\left[\prod\limits _{j=0}^{i}\left( 1-\frac{j}{N}\right)\right]\\ & =\frac{1}{N}\sum\nolimits _{i=0}^{N-1}\frac{( N-1) !( i+1)}{( N-i-1) !N^{i}}\\ & =\frac{1}{N}\sum\nolimits _{i=1}^{N}\frac{i\times N!}{( N-i) !N^{i}} \end{aligned}\\用Mathematica验算了一下,还真是对的。

回到我们的主题,把 πi=π0[j=0i(1jc)]\displaystyle \pi _{i} =\pi _{0}\left[\prod\nolimits _{j=0}^{i}( 1-jc)\right] 代入 i=0N1πi=1\displaystyle \sum\nolimits _{i=0}^{N-1} \pi _{i} =1,得到 π0{i=0N1[j=0i(1jc)]}=1\displaystyle \pi _{0}\left\{\sum\nolimits _{i=0}^{N-1}\left[\prod\nolimits _{j=0}^{i}( 1-jc)\right]\right\} =1,所以π0=1i=0N1[j=0i(1jc)]πi=π0[j=0i(1jc)]=j=0i(1jc)k=0N1[j=0k(1jc)], i=0,1,,N1\begin{aligned} \pi _{0} & =\frac{1}{\sum\limits _{i=0}^{N-1}\left[\prod\nolimits _{j=0}^{i}( 1-jc)\right]}\\ \pi _{i} & =\pi _{0}\left[\prod\limits _{j=0}^{i}( 1-jc)\right] =\frac{\prod\limits _{j=0}^{i}( 1-jc)}{\sum\limits _{k=0}^{N-1}\left[\prod\limits _{j=0}^{k}( 1-jc)\right]} ,\ i=0,1,\cdots ,N-1 \end{aligned}\\方法1和方法2的结果肯定是相同的,所以我们惊讶地发现,方法1中那个看起来没法化简的公式,事实上有个稍微简单一点的形式E=ck=1N1k2[j=0k1(1jc)]+Nj=0N1(1jc)i=0N1[j=0i(1jc)]E=c\sum _{k=1}^{N-1} k^{2}\left[\prod _{j=0}^{k-1}( 1-jc)\right] +N\prod _{j=0}^{N-1}( 1-jc) \equiv \sum\limits _{i=0}^{N-1}\left[\prod\limits _{j=0}^{i}( 1-jc)\right]\\再次惊了,这两个 c\displaystyle c 的多项式居然是一样的,数值验证了一下确实相等,我们又证明了一个怪怪恒等式。

补充内容:我知道这个等式怎么证明了,对于取值为非负整数的随机变量 X\displaystyle X,一定有如下等式
E(X)=n=1nP(X=n)=n=1P(Xn)E( X) =\sum _{n=1}^{\infty } nP( X=n) =\sum _{n=1}^{\infty } P( X\geqslant n)\\上式就是把 P(X=n)\displaystyle P( X=n)P(Xn)\displaystyle P( X\geqslant n) 分别代入的结果,这个化简可以在方法1中就得到。

写代码时用右端的公式应该会稍微简单一点,在计算大于等于的概率时,循环中每一步只需要一次乘法与一次加法。我去翻了很多介绍PRD算法的文章,都是用方法1里的公式来编写代码的。只有一个例外,

其中的一个答案也说了用Markov链可以解释PRD算法,他的代码是构造完转移矩阵 P\displaystyle P 后,计算了矩阵 P\displaystyle P 最大特征值 1\displaystyle 1 对应的唯一特征向量,并归一化,理论上这与我们的结果也是一样的,但是看起来计算复杂度也会更高一些。

然后我们再用高贵的Mathematica帮我们化简一些特殊情形,取 c=1/N\displaystyle c=1/N 得到 E=i=0N1[j=0i(1jN)]=1Ni=0N1N!(Ni1)!Ni=N(eN)NNxN1ex dx (Mathematica)\begin{aligned} E & =\sum\limits _{i=0}^{N-1}\left[\prod\limits _{j=0}^{i}\left( 1-\frac{j}{N}\right)\right]\\ & =\frac{1}{N}\sum\limits _{i=0}^{N-1}\frac{N!}{( N-i-1) !N^{i}}\\ & =N\left(\frac{e}{N}\right)^{N}\int _{N}^{\infty } x^{N-1} e^{-x} \ \mathrm{d} x\ \cdots ( Mathematica) \end{aligned}\\

这大概是把阶乘写成留数以后化简得到的吧。

容易看出 E=i=0N1[j=0i(1jc)]\displaystyle E=\sum\nolimits _{i=0}^{N-1}\left[\prod\nolimits _{j=0}^{i}( 1-jc)\right]c[1/N,1/(N1))\displaystyle c\in [ 1/N,1/( N-1)) 上递减,所以名义概率 p=π0=1/Ep=\pi _{0} =1/E 确实是 c\displaystyle c 的增函数,可以放心使用二分查找。

PRD机制在低概率下的表现

保底机制本意是不希望大家的运气太差,出现连续失败的情况。在独立随机中,如果名义概率 p\displaystyle p 比较大,那么出现连续失败的概率也不会太大(当然如果遇到了的话肯定还是非常不爽的)。而如果名义概率 p\displaystyle p 本身就较低,在独立情况下,连续失败将不可避免。假设 T\displaystyle T 是名义概率 p\displaystyle p 下首次成功的时间。在独立的情况下, T\displaystyle T 服从几何分布 Geo(p)\displaystyle Geo( p),且对于正整数 t\displaystyle t P(T>t|)=(1p)tP( T >t|独立) =( 1-p)^{t}\\对于较大的实数 t\displaystyle t,将上式中的等号改成约等于也是成立的。平均 E(T)=1/p\displaystyle E( T) =1/p 次会成功一次,我们来估算一下在 2\displaystyle 2 倍期望次数后都没有成功的概率 P(T>2p|)(1p)2pe20.135335P\left( T >\frac{2}{p} |独立\right) \approx ( 1-p)^{\frac{2}{p}} \approx e^{-2} \approx 0.135335\\上式第二个约等号来自于高数中最基础的一个极限 limn(1+1n)n=e \lim _{n\rightarrow \infty }\left( 1+\frac{1}{n}\right)^{n} =e\\这说明在独立随机中,如果概率 p\displaystyle p 比较小,那大概有 13.5%\displaystyle 13.5\% 的人在 2\displaystyle 2 倍期望以后才成功,将上面的 2\displaystyle 2 改成其他数字也是成立的,大概会有 e34.98%\displaystyle e^{-3} \approx 4.98\% 的玩家在 3\displaystyle 3 倍期望, e41.83%\displaystyle e^{-4} \approx 1.83\% 的玩家在$\displaystyle 4$倍期望, e50.67%\displaystyle e^{-5} \approx 0.67\% 的玩家在 5\displaystyle 5 倍期望后才成功。。。。这个比例可以说非常高了,如果一个游戏有几万个玩家,那这部分玩家会给游戏带来大量的差评。

在PRD算法下,运气最差会在第 N=1/c1/c\displaystyle N=\lceil 1/c\rceil \approx 1/c 时成功,也就是说 P(T>N|PRD)=0\displaystyle P( T >N|\mathrm{PRD}) =0,那独立情况下这个概率是多大呢  P(T>N|)(1p)N(1p)1c=(1p)1ppcepc \ P( T >N|独立) \approx ( 1-p)^{N} \approx ( 1-p)^{\frac{1}{c}} =( 1-p)^{\frac{1}{p}\frac{p}{c}} \approx e^{-\frac{p}{c}}\\p/c\displaystyle p/c 是多少呢,首先一个显然的结论是 c\displaystyle c 一定是严格小于名义概率 p\displaystyle p 的,可以回去看一下那张表格感受一下 c\displaystyle cp\displaystyle p 之间的差距,在 p0.4\displaystyle p\leqslant 0.4 时, p/c2\displaystyle p/c\geqslant 2,名义概率越小, p/c\displaystyle p/c 越大,相应的  P(T>N|)\displaystyle \ P( T >N|独立) 也越接近于 0\displaystyle 0。 例如 p=0.05\displaystyle p=0.05 时, c0.00380166\displaystyle c\approx 0.00380166N=264\displaystyle N=264,PRD机制只能保证最坏 264\displaystyle 264 次才成功的情况,这个数是平均次数 1/0.05=20\displaystyle 1/0.05=2013.2\displaystyle 13.2 倍,在这么大的 N\displaystyle NP(T>N|)1.3153×106\displaystyle P( T >N|独立) \approx 1.3153\times 10^{-6},本身已经很小了。也就说 p=0.05\displaystyle p=0.05 的情况下,PRD算法与独立算法相比,好像并未有效地进行保底。而且我们取的 p=0.05\displaystyle p=0.05 其实并不算很小,在很多抽卡游戏里,名义概率比这小得多,在这种情况下,使用PRD算法,将会导致灾难性的体验,并不会比独立随机好太多。

如果之后有空的话,我们来比较一下PRD算法给出的成功耗时分布与独立情形对应的几何分布,算算他们之间全变差或者Wasenstein距离,看看他们的差距到底有多大,是否真的起到了良好的保底效果。

保底机制的扩展

原神的 1.6%\displaystyle 1.6\% 概率对应平均 62.5\displaystyle 62.5 次出五星,而原神给的保底次数是 90\displaystyle 90 次,也就是说最大次数大概是平均次数的 1.5\displaystyle 1.5 倍,我觉得这也是玩家比较能接受的数值,因为如果这个倍数再大一些,就会有一部分运气差的玩家觉得自己与平均水平的严重偏离。我们可以认为,如果一个保底算法的最大次数不超过平均次数的 1.5\displaystyle 1.5 倍是比较合理的。我们来看看在这个意义下PRD算法在什么情况下是合理的,回去翻看表格,会得到一个很意外的情况,差不多当 p>0.55\displaystyle p >0.55 时,这个保底机制才是合理的,在Dota2中只有很少的技能触发概率超过 0.55\displaystyle 0.55。即使我们将比例从 1.5\displaystyle 1.5 放宽到 2\displaystyle 2,那也只能因对名义概率 p>0.4\displaystyle p >0.4 的情形。而幻刺的恩赐解脱 p=0.15\displaystyle p=0.15c0.0322\displaystyle c\approx 0.0322,采用PRD算法,在最极端情况下会出现连续31次不暴击(概率为 6.661338×1016\displaystyle 6.661338\times 10^{-16}),一局比赛对英雄的攻击次数可能也就 200300\displaystyle 200\sim 300 次左右,出现这种极端情形将会严重影响游戏体验。假如在刚暴击过一次之后立刻去参加团战,一次团战如果攻击 10\displaystyle 10 次,这 10\displaystyle 10 次都不暴击的概率高达 13.33755%\displaystyle 13.33755\%15\displaystyle 15 次攻击不出暴击的概率也有 0.8704117%\displaystyle 0.8704117\%,一次团战可能就失败了。PRD算法并没有很好地完成减少极端情况的任务,究其原因,是PRD算法的自由度太低了,在给定名义概率 p\displaystyle p 后, c\displaystyle cN\displaystyle N 的值都可以完全确定下来。在这个意义下,PRD算法可能还不如我在上一篇文章中介绍的简单保底机制,因为在那里,给定名义概率后 p\displaystyle p,保底次数 N\displaystyle N 可以自由选取,只需满足 N1/p\displaystyle N\geqslant 1/p 即可, p\displaystyle pN\displaystyle N 固定后, c\displaystyle c 的值可以通过公式计算出来,我们可以把 N\displaystyle N 选的比较靠近期望次数 1/p\displaystyle 1/p 来减少极端情况,不过这样可能会导致算法固定在第 N\displaystyle N 次左右才给出成功结果。

得想办法前面的保底机制进行修改,让最大次数 N\displaystyle N 不要比平均次数 1/p\displaystyle 1/p 大太多,同时表现出一定的随机性,让人不要太容易预测。我在这里给出一种可能得解决方案。回顾简单保底机制的转移矩阵 P=(c1cc1cc1cc1c10)012N2N1P=\begin{pmatrix} c & 1-c & & & & & \\ c & & 1-c & & & & \\ c & & & 1-c & & & \\ \vdots & & & & \ddots & & \\ c & & & & & 1-c & \\ 1 & & & & & 0 & \end{pmatrix}\begin{matrix} 0\\ 1\\ 2\\ \vdots \\ N-2\\ N-1 \end{matrix}\\以及本文里的PRD算法对应的转移矩阵,这两个矩阵长得很像,在相同的位置为 0\displaystyle 0 或非 0\displaystyle 0,差别只是非 0\displaystyle 0 位置的数字,我们可以修改这些数字,不需要拘泥于常数,或者线性增长,采用非线性的变化,只需要满足修改后还是一个不可约的转移矩阵即可,修改后的名义概率可以用这两篇文章里展示的两种方法计算。一个例子就是原神抽卡,前一段是常数,后一段线性增长,我们会在之后的文章中对原神的抽卡机制进行分析。更进一步,甚至可以将一部分 0\displaystyle 0 改成非 0\displaystyle 0,增加过程的随机性,但这样的修改需要非常仔细,因为这可能会导致保底次数 N\displaystyle N 的失效,也就是无法保证 N\displaystyle N 次之内一定成功。最后再配合上上一篇文章提到的将不同状态分配给不同的随机结果(不同稀有度),这会给出非常有趣的保底机制。

爱笑的男孩运气不会太差,但也不会太好

我们一直拿Dota2举例,所以这次我们来算一个Moba玩家可能会关心的问题,在PRD机制下,如果名义概率为 p\displaystyle p,那连续两次暴击的概率是多少,在独立随机的情况下,连续两次暴击的概率显然是 p2\displaystyle p^{2}。会有人问PRD算法既然是一种保底算法,让我们的运气不要太差,那是不是也会让连续成功概率变高呢。这里先给出结论,答案是否定的,事实上我们可以证明在连续两次暴击的出现频率会趋向于 pc\displaystyle pc,而 c<p\displaystyle c< p,所以连续两次暴击的概率是严格小于独立随机的情形的,非常可惜,好运也同时被PRD算法削减了。连续 k\displaystyle k 次暴击的概率是 pck1\displaystyle pc^{k-1},严格小于独立情形中的 pk\displaystyle p^{k}k\displaystyle k 越大差距越大。这些概率的计算方法将会放在之后的文章里。

当然我们也不要太消极,对于PRD算法,在参加团战之前,我们可以多攻击几次,让自己处于一个较大的状态 k\displaystyle k,后续短期的团战中,暴击概率高于名义概率 p\displaystyle p,最低要求是 (k+1)cp\displaystyle ( k+1) c\geqslant p,例如幻刺需要在上一次暴击后积攒至少 4\displaystyle 4 次不暴击,点了 22%\displaystyle 22\% 暴击天赋的话减少为 3\displaystyle 3 次。需要注意是至少这么多次,积攒的越多,团战暴击概率越高,当然积攒多次不暴击的难度也会变大。

暂定的后续内容一篇是关于原神的抽卡机制,还有一篇是一些零碎的内容。

编辑于 2023-06-09 21:25・上海
写下你的评论...

2 条评论
默认
最新
梅川酷子
为什么这么好的回答没人赞呢 统计太小众了嘛[思考]
2024-08-02 · 美国
映客

看不懂思密达

2024-05-07 · 浙江
想来知乎工作?请发送邮件到 jobs@zhihu.com
登录即可查看 超5亿 专业优质内容
超 5 千万创作者的优质提问、专业回答、深度文章和精彩视频尽在知乎。
登录即可查看 超5亿 专业优质内容
超 5 千万创作者的优质提问、专业回答、深度文章和精彩视频尽在知乎。